home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
GVECTORS.ZIP
/
GVECTORS.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-28
|
22KB
|
759 lines
// ┌────────────────────────────────────────┐
// │ Gouraud Vectors │
// │ coded by Tumblin / Bodies In Motion │
// │ (Terry Sznober) │
// │ Copyright (c) 1995 by Bodies In Motion │
// └────────────────────────────────────────┘
//────────────────────────────────────────────────────────────────────────
// Last modified May 28, 1995
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
#include <dos.h>
#include <process.h>
#include <xlib_all.h>
#include "gvectors.h"
//──────────────────────── fixed point math code ─────────────────────────
typedef long Fixedpoint;
#define Int2Fixed(a) (Fixedpoint)((Fixedpoint)(a) << 16)
#define Fixed2Int(a) (int)((a) >> 16)
#define Float2Fixed(a) ((Fixedpoint)((a) * 65536.0))
#define Fixed2Float(a) ((a) / 65536.0)
extern "C" {
Fixedpoint FixedMul(Fixedpoint,Fixedpoint);
Fixedpoint FixedDiv(Fixedpoint,Fixedpoint);
};
//--------------------------------------------------------------------------
// maximum # of degrees for sin and cos tables
#define MAXDEGREES 720
// distance from users eye to screen surface in pixels
#define EYE_DISTANCE Int2Fixed(256)
//──────────────────────────── data structures ───────────────────────────
char filename[80]; // filename of object to load/save
Fixedpoint cosine[MAXDEGREES]; // cosine lookup table
Fixedpoint sine[MAXDEGREES]; // sine lookup table
// these are for the gouraud shading routines
int edge_l[240];
int edge_r[240];
char color_l[240];
char color_r[240];
#define MAXVERTICES 300
#define MAXPOLYGONS 300
#define MAXPOLYVERT 20
// structure of one vertex
typedef struct
{
Fixedpoint Vox,Voy,Voz; // vertex original coordinates
Fixedpoint Vwx,Vwy,Vwz; // vertex working coordinates
int Vsx,Vsy,Vsz; // vertex screen coordinates
Fixedpoint Nox,Noy,Noz; // normal's original coordinates
Fixedpoint Nwx,Nwy,Nwz; // normal's working coordinates
int shadedcolor; // color of vertex used for gouraud shading
} VertexTYPE;
// structure of one polygon
typedef struct
{
int NumOfVertices; // number of vertices that make up the polygon
int Vertex[MAXPOLYVERT]; // vertex indices in counter clockwise order
int zcenter; // center z coordinate (used for polygon sorting)
int color; // color group of polygon (before shading)
int shadedcolor; // actual color value of polygon after shading
} PolygonTYPE;
// object structure
typedef struct
{
Fixedpoint Ox,Oy,Oz; // coordinates of object's origin
int Ax,Ay,Az; // object's rotation angles
int NumOfVertices; // number of vertices in object
VertexTYPE Vertex[MAXVERTICES]; // all vertices in object
int NumOfPolygons; // number of polygons in object
PolygonTYPE Polygon[MAXPOLYGONS]; // all polygons in object
} ObjectTYPE;
ObjectTYPE Object; // one object
int NumOfSortedPolygons; // number of sorted visible polygons
int PolygonOrderList[MAXPOLYGONS]; // list of polygon indices for qsorting
// structure of light source
typedef struct
{
Fixedpoint x,y,z; // coodinates of light source
Fixedpoint wx,wy,wz; // working (intermediate) coordinates
int ax,ay,az; // rotation angles
} LightSourceTYPE;
LightSourceTYPE LightSource; // one light source
int eboxtop1; // erase box top coordinate (current frame)
int eboxbottom1; // erase box bottom coordinate (current frame)
int eboxleft1; // erase box left coordinate (current frame)
int eboxright1; // erase box right coordinate (current frame)
int eboxtop2; // erase box top coordinate (one frame ago)
int eboxbottom2; // erase box bottom coordinate (one frame ago)
int eboxleft2; // erase box left coordinate (one frame ago)
int eboxright2; // erase box right coordinate (one frame ago)
int eboxtop3; // erase box top coordinate (two frames ago)
int eboxbottom3; // erase box bottom coordinate (two frames ago)
int eboxleft3; // erase box left coordinate (two frames ago)
int eboxright3; // erase box right coordinate (two frames ago)
// dot product, used for light source shading
Fixedpoint DotProduct;
//──────────────────────── function prototypes ───────────────────────────
int main(void);
void CreateLookupTables(void);
void RotateObject(void);
void LoadObject(void);
void SortPolygons(void);
int ComparePolygons(const void *a, const void *b);
void DrawPolygons(void);
void CalculateColor(void);
void SetupLightSource(void);
void RotateNormals(void);
void RotateLightSource(void);
void CalculateNormals(void);
void GTriangle(float,float,float, float,float,float, float,float,float,int);
void ScanEdge(float,float,float,float,float,float);
//──────────────── code to create sin and cos lookup tables ──────────────
void CreateLookupTables(void)
{
int i;
for(i=0; i<MAXDEGREES; i++)
{
cosine[i]=Float2Fixed(cos((float)i*360/MAXDEGREES * 3.14159265 / 180.0));
sine[i] =Float2Fixed(sin((float)i*360/MAXDEGREES * 3.14159265 / 180.0));
}
}
//─────────────────────────────── rotate object ───────────────────────
void RotateObject(void)
{
int i;
Fixedpoint nx,ny,nz,cosxangle,sinxangle,cosyangle,sinyangle,coszangle,sinzangle;
VertexTYPE *vert; // pointer to a vertex structure
// get the sine and cosine angles to save time from table lookup
sinxangle=sine[Object.Ax];
cosxangle=cosine[Object.Ax];
sinyangle=sine[Object.Ay];
cosyangle=cosine[Object.Ay];
sinzangle=sine[Object.Az];
coszangle=cosine[Object.Az];
// initialize the erase-box info to the extreme values
eboxtop1=240;
eboxleft1=320;
eboxright1=0;
eboxbottom1=0;
// loop through all vertices in object
for(i=0;i<Object.NumOfVertices;i++)
{
vert=&Object.Vertex[i];
// rotate around the x-axis
vert->Vwz=FixedMul(vert->Voy,cosxangle) - FixedMul(vert->Voz,sinxangle);
vert->Vwy=FixedMul(vert->Voy,sinxangle) + FixedMul(vert->Voz,cosxangle);
vert->Vwx=vert->Vox;
// rotate around the y-axis
nx=FixedMul(vert->Vwx,cosyangle) - FixedMul(vert->Vwz,sinyangle);
nz=FixedMul(vert->Vwx,sinyangle) + FixedMul(vert->Vwz,cosyangle);
vert->Vwx=nx;
vert->Vwz=nz;
// rotate around the z-axis
nx=FixedMul(vert->Vwx,coszangle) - FixedMul(vert->Vwy,sinzangle);
ny=FixedMul(vert->Vwx,sinzangle) + FixedMul(vert->Vwy,coszangle);
vert->Vwx=nx;
vert->Vwy=ny;
// project the 3-D coordinates to screen coordinates
vert->Vsx=Fixed2Int(FixedMul(FixedDiv(vert->Vwx+Object.Ox,vert->Vwz+Object.Oz),EYE_DISTANCE)) + 160;
vert->Vsy=Fixed2Int(FixedMul(FixedDiv(vert->Vwy+Object.Oy,vert->Vwz+Object.Oz),EYE_DISTANCE)) + 120;
vert->Vsz=Fixed2Int(vert->Vwz+Object.Oz);
// while we are at it, update the erase-box information
if(vert->Vsx < eboxleft1) eboxleft1 = vert->Vsx;
if(vert->Vsx > eboxright1) eboxright1 = vert->Vsx;
if(vert->Vsy < eboxtop1) eboxtop1 = vert->Vsy;
if(vert->Vsy > eboxbottom1) eboxbottom1 = vert->Vsy;
}
// clip the erase box
if(eboxleft1<0) eboxleft1=0;
if(eboxtop1<0) eboxtop1=0;
if(eboxright1>319) eboxright1=319;
if(eboxbottom1>239) eboxbottom1=239;
}
//─────────────────────── load object─────────────────────────────────────
void LoadObject(void)
{
FILE *file;
char filename[80];
int i,j,temp,result;
VertexTYPE *vert; // pointer to a vertex structure
printf("LOAD OBJECT\n===========\n\n");
// show the current directory's .V10 files
result = system("dir *.v10 /w/p");
if (result)
{
printf("\nSorry but couldn't list the V10 files of the current directory\n");
}
// get the filename from user
printf("\n\nEnter filename (filename.V10) : ");
gets(filename);
// open the file to write to it
if ((file = fopen(filename,"rb")) == NULL)
{
printf("\n\nCannot open input file.\n");
}
else
{
// okay file is ready to read data from it
// read number of dots in file
fread(&Object.NumOfVertices,sizeof(int),1,file);
// read in all of the object's vertices
for(i=0;i < Object.NumOfVertices; i++)
{
fread(&temp,sizeof(int),1,file);
Object.Vertex[i].Vox=Int2Fixed(temp);
fread(&temp,sizeof(int),1,file);
Object.Vertex[i].Voy=Int2Fixed(temp);
fread(&temp,sizeof(int),1,file);
Object.Vertex[i].Voz=Int2Fixed(temp);
}
// read in the number of polygons
fread(&Object.NumOfPolygons,sizeof(int),1,file);
// read in the information for each polygon
for(i=0; i < Object.NumOfPolygons; i++)
{
// read in the number of vertices in polygon
fread(&Object.Polygon[i].NumOfVertices,sizeof(int),1,file);
// read in the polygon's vertices (already in counter clockwise order)
for(j=0; j< Object.Polygon[i].NumOfVertices; j++)
{
fread(&Object.Polygon[i].Vertex[j],sizeof(int),1,file);
}
// read the color of the polygon
fread(&Object.Polygon[i].color,sizeof(int),1,file);
}
// we're finished, close the file
fclose(file);
printf("\n\nObject loaded.\n");
}
}
//────────────────────────── make visible polygon list ─────────────────────
void MakePolygonList(void)
{
int i,j,k;
VertexTYPE *v0,*v1,*v2;
j=0;
for(i=0;i<Object.NumOfPolygons;i++)
{
v0=&Object.Vertex[Object.Polygon[i].Vertex[0]];
v1=&Object.Vertex[Object.Polygon[i].Vertex[1]];
v2=&Object.Vertex[Object.Polygon[i].Vertex[2]];
// if expression results in a negative then polygon is visible
if( ((v1->Vsx - v0->Vsx) * (v2->Vsy - v0->Vsy) - (v1->Vsy - v0->Vsy) * (v2->Vsx - v0->Vsx)) < 0 )
{
PolygonOrderList[j++]=i;
}
}
NumOfSortedPolygons=j;
}
//──────────────────────────────────── sort polygons ───────────────────────
void SortPolygons(void)
{
int i,j;
int maxz,minz;
int temp;
PolygonTYPE *poly;
// first find the distance of each polygon (from midpoint of max & min z's)
for(i=0;i<Object.NumOfPolygons;i++)
{
poly=&Object.Polygon[i];
minz=65535; // set to extreme values
maxz=-65536; // set to extreme values
for(j=0;j<poly->NumOfVertices;j++)
{
if(Object.Vertex[poly->Vertex[j]].Vsz < minz)
{
minz=Object.Vertex[poly->Vertex[j]].Vsz;
}
if(Object.Vertex[poly->Vertex[j]].Vsz > maxz)
{
maxz=Object.Vertex[poly->Vertex[j]].Vsz;
}
}
// now calculate the distance
poly->zcenter=(maxz+minz)>>1;
}
// qsort the polygons
qsort((const void *)PolygonOrderList,NumOfSortedPolygons,sizeof(PolygonOrderList[0]),ComparePolygons);
}
//────────────────────────── compare zcenter (for qsort) ───────────────────
int ComparePolygons(const void *a, const void *b)
{
if( Object.Polygon[*(int *)a].zcenter < Object.Polygon[*(int *)b].zcenter )
{
return -1;
}
else if( Object.Polygon[*(int *)a].zcenter > Object.Polygon[*(int *)b].zcenter )
{
return +1;
}
else
{
return 0;
}
}
//──────────────────────────────────── draw polygons ───────────────────────
void DrawPolygons(void)
{
int i,j;
x_rect_fill(eboxleft3,eboxtop3,eboxright3+1,eboxbottom3+1,HiddenPageOffs,0);
for(i=0;i<NumOfSortedPolygons;i++)
{
// loop through all vertices in polygon and draw triangular pieces
for(j=0; j < Object.Polygon[PolygonOrderList[i]].NumOfVertices-2;j++)
{
GTriangle((float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[0]].Vsx,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[0]].Vsy,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[0]].shadedcolor,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[j+1]].Vsx,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[j+1]].Vsy,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[j+1]].shadedcolor,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[j+2]].Vsx,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[j+2]].Vsy,
(float)Object.Vertex[Object.Polygon[PolygonOrderList[i]].Vertex[j+2]].shadedcolor,
HiddenPageOffs);
}
}
// update the older erase box information
eboxtop3 = eboxtop2; eboxtop2 = eboxtop1;
eboxleft3 = eboxleft2; eboxleft2 = eboxleft1;
eboxright3 = eboxright2; eboxright2 = eboxright1;
eboxbottom3 = eboxbottom2; eboxbottom2 = eboxbottom1;
}
//──────────────────────────── calculate normals ─────────────────────────
void CalculateNormals(void)
{
double xlen,ylen,zlen,length;
//This will calculate the normals of the vertices for gouraud shading
for(int i=0;i < Object.NumOfVertices; i++)
{
// calculate perpendicular via the vertex and the vertex*2
// ie. we are scaling the vertices and use them as the new vertices
// for gouraud shading. This is not quite correct though, because
// you wouldn't be accounting for polygons that face the object's
// center.
xlen = Fixed2Float(FixedMul(Object.Vertex[i].Vox,Int2Fixed(2)));
ylen = Fixed2Float(FixedMul(Object.Vertex[i].Voy,Int2Fixed(2)));
zlen = Fixed2Float(FixedMul(Object.Vertex[i].Voz,Int2Fixed(2)));
// calculate the length of the normal
length = sqrt(xlen*xlen + ylen*ylen + zlen*zlen);
// scale it to a unit normal
Object.Vertex[i].Nox = Float2Fixed(xlen / length);
Object.Vertex[i].Noy = Float2Fixed(ylen / length);
Object.Vertex[i].Noz = Float2Fixed(zlen / length);
}
}
//──────────────────────────── calculate color ───────────────────────────
void CalculateColor(void)
{
int i;
int color;
for(i=0; i < Object.NumOfVertices; i++)
{
DotProduct=FixedMul(Object.Vertex[i].Nwx , LightSource.wx) +
FixedMul(Object.Vertex[i].Nwy , LightSource.wy) +
FixedMul(Object.Vertex[i].Nwz , LightSource.wz);
Object.Vertex[i].shadedcolor = 56 + Fixed2Int(FixedMul(DotProduct,Int2Fixed(40)));
}
}
//──────────────────────────── rotate normals ─────────────────────────────
void RotateNormals(void)
{
int i;
Fixedpoint nx,ny,nz,cosxangle,sinxangle,cosyangle,sinyangle,coszangle,sinzangle;
VertexTYPE *vert; // pointer to a vertex structure
// get sine and cosine angles to save time from table lookup in inner loop
sinxangle=sine[Object.Ax];
cosxangle=cosine[Object.Ax];
sinyangle=sine[Object.Ay];
cosyangle=cosine[Object.Ay];
sinzangle=sine[Object.Az];
coszangle=cosine[Object.Az];
for(i=0;i<Object.NumOfVertices;i++)
{
vert=&Object.Vertex[i];
// rotate around the x-axis
vert->Nwz=FixedMul(vert->Noy , cosxangle) - FixedMul(vert->Noz , sinxangle);
vert->Nwy=FixedMul(vert->Noy , sinxangle) + FixedMul(vert->Noz , cosxangle);
vert->Nwx=vert->Nox;
// rotate around the y-axis
nx=FixedMul(vert->Nwx , cosyangle) - FixedMul(vert->Nwz , sinyangle);
nz=FixedMul(vert->Nwx , sinyangle) + FixedMul(vert->Nwz , cosyangle);
vert->Nwx=nx;
vert->Nwz=nz;
// rotate around the z-axis
nx=FixedMul(vert->Nwx , coszangle) - FixedMul(vert->Nwy , sinzangle);
ny=FixedMul(vert->Nwx , sinzangle) + FixedMul(vert->Nwy , coszangle);
// reverse the direction of the normals for lightsource shading
vert->Nwx=-nx;
vert->Nwy=-ny;
vert->Nwz=-nz;
}
}
//───────────────────── set up light source coordinates ──────────────────
void SetupLightSource(void)
{
double xlen,ylen,zlen,length;
// assign the delault light source position
xlen=0;
ylen=0;
zlen=10;
// calculate the length of the vector (light source to 0,0,0)
length = sqrt(xlen*xlen + ylen*ylen + zlen*zlen);
// scale it to a unit vector
LightSource.x = Float2Fixed(xlen/length);
LightSource.y = Float2Fixed(ylen/length);
LightSource.z = Float2Fixed(zlen/length);
}
//────────────────────── rotate light source ─────────────────────────────
void RotateLightSource(void)
{
int i;
Fixedpoint nx,ny,nz;
Fixedpoint cosxangle,sinxangle,cosyangle,sinyangle,coszangle,sinzangle;
// update the light source rotation angles
LightSource.ax+=8; if(LightSource.ax>=MAXDEGREES) LightSource.ax=0;
LightSource.ay+=8; if(LightSource.ay>=MAXDEGREES) LightSource.ay=0;
LightSource.az+=8; if(LightSource.az>=MAXDEGREES) LightSource.az=0;
// get sine and cosine angles to save time from table lookup in inner loop
sinxangle=sine[LightSource.ax];
cosxangle=cosine[LightSource.ax];
sinyangle=sine[LightSource.ay];
cosyangle=cosine[LightSource.ay];
sinzangle=sine[LightSource.az];
coszangle=cosine[LightSource.az];
// rotate around the x-axis
LightSource.wz=FixedMul(LightSource.y , cosxangle) - FixedMul(LightSource.z , sinxangle);
LightSource.wy=FixedMul(LightSource.y , sinxangle) + FixedMul(LightSource.z , cosxangle);
LightSource.wx=LightSource.x;
// rotate around the y-axis
nx=FixedMul(LightSource.wx, cosyangle) - FixedMul(LightSource.wz , sinyangle);
nz=FixedMul(LightSource.wx, sinyangle) + FixedMul(LightSource.wz , cosyangle);
LightSource.wx=nx;
LightSource.wz=nz;
// rotate around the z-axis
nx=FixedMul(LightSource.wx , coszangle) - FixedMul(LightSource.wy , sinzangle);
ny=FixedMul(LightSource.wx , sinzangle) + FixedMul(LightSource.wy , coszangle);
// reverse the direction of the normals for lightsource shading
LightSource.wx=-nx;
LightSource.wy=-ny;
LightSource.wz=-nz;
}
//─────────────────────────── MAIN PROGRAM ───────────────────────────────
int main(void)
{
int y;
char keypress;
clrscr();
printf("GOURAUD VECTORS\n by Tumblin / Bodies In Motion '95\n");
LoadObject();
printf("\n\nPress any key to begin, and again to end.\n\n");
getch();
CreateLookupTables();
SetupLightSource();
CalculateNormals();
x_text_init();
x_set_mode(X_MODE_320x240,320);
x_set_doublebuffer(240);
x_set_cliprect(0,0,79,239);
x_put_pal_raw(palette,256,0); // setup VED 1.0 palette colors
// initialize the position and angles of object
Object.Ox=Int2Fixed(0);
Object.Oy=Int2Fixed(0);
Object.Oz=Int2Fixed(-1024);
Object.Ax=0;
Object.Ay=0;
Object.Az=0;
// run the demo until user hits key
do
{
Object.Ax+=8; if(Object.Ax >=MAXDEGREES) Object.Ax=0;
Object.Ay-=8; if(Object.Ay <0) Object.Ay=MAXDEGREES-1;
Object.Az+=6; if(Object.Az >=MAXDEGREES) Object.Az=0;
RotateObject();
RotateNormals();
RotateLightSource();
CalculateColor();
MakePolygonList();
SortPolygons();
DrawPolygons();
x_page_flip(0,0);
}while(!kbhit());
getch();
x_text_mode();
printf("GOURAUD VECTORS\n");
printf("coded by Tumblin / Bodies In Motion '95\n");
printf("(aka Terry Sznober)\n");
printf("You can email me at tumblin@mi.net\n");
printf("Greetings to everyone in the demo scene!\n");
printf("Special greets to all the cool people I met at NAID '95!\n");
return(0);
}
//--------------------- Gouraud Shaded Triangle routine ------------------
void GTriangle(float x1,float y1,float c1,
float x2,float y2,float c2,
float x3,float y3,float c3,
int pageoffs)
{
float mx; // slope dy/dx
float mc; // slope dc/dx
float x; // x screen coordinate
float c; // color of pixel to plot
int count; // looping variable
int y; // looping variable
float cl; // color of left edge of horizontal scanline
float cr; // color of right edge of horizontal scanline
float el; // coordinate of left edge of horizontal scanline
float er; // coordinate of right edge of horizontal scanline
// initialize the edge buffers to extreme values
for(count=0;count<240;count++)
{
edge_l[count]=319;
edge_r[count]=0;
}
// scan the edges of the triangle
ScanEdge(x1,y1,c1, x2,y2,c2);
ScanEdge(x2,y2,c2, x3,y3,c3);
ScanEdge(x3,y3,c3, x1,y1,c1);
// gouraud fill the horizontal scanlines
for(y=0;y<240;y++)
{
// if the scanline is valid (ie left coordinate < right coordinate)
if(edge_l[y] <= edge_r[y])
{
cl=(float)color_l[y];
cr=(float)color_r[y];
el=(float)edge_l[y];
er=(float)edge_r[y];
// now calculate slope of color (dc/dx)
if( (er-el) == 0)
{
// denominator will be zero so don't do the division so use dc
mc = (cr-cl);
}
else
{
// denominator okay, so use dc/dx
mc = (cr-cl)/(er-el);
}
c = cl; // start with color of left edge of scanline
// loop through every pixel in horizontal scanline
for(count=(int)edge_l[y]; count < (int)edge_r[y]; count++)
{
x_put_pix(count,y,pageoffs,(int)c);
c+=mc; // increase the color by dc/dx
}
}
}
}
void ScanEdge(float x1,float y1,float c1,float x2,float y2,float c2)
{
// scan-convert (x1,y1,c1)-(x2,y2,c2)
float mx; // slope dx/dy
float mc; // slope dc/dy
float x; // x pixel coordinate
float c; // pixel's color
int count; // looping variable
float temp; // used for swapping
// make sure that edge goes from top to bottom
if( (y2-y1) < 0 )
{
// swap y2 with y1
temp=y1;
y1=y2;
y2=temp;
// swap x2 with x1
temp=x1;
x1=x2;
x2=temp;
// swap c2 with c1
temp=c1;
c1=c2;
c2=temp;
}
// initialize for stepping
if( (y2-y1) != 0)
{
mx = (x2-x1)/(y2-y1); // dx/dy
mc = (c2-c1)/(y2-y1); // dc/dy
}
else
{
mx = (x2-x1); // dx
mc = (c2-c1); // dc
}
x = x1; // starting x coordinate
c = c1; // starting c color
// step through edge and record color values along the way
for(count=y1; count < y2; count++)
{
if(x < (float)edge_l[count])
{
edge_l[count]=(int)x;
color_l[count]=(int)c;
}
if(x > (float)edge_r[count])
{
edge_r[count]=(int)x;
color_r[count]=(int)c;
}
x+=mx; // x = x + dx/dy
c+=mc; // c = c + dc/dy
}
}